package com.example.glesstudy;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.util.Size;
import android.view.Surface;

import androidx.annotation.NonNull;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.core.SurfaceRequest;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.content.ContextCompat;
import androidx.core.util.Consumer;
import androidx.lifecycle.LifecycleOwner;

import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class CameraXHelper {
    private static final String TAG = "CameraXHelper";
    private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10, 10L,
            TimeUnit.SECONDS, new LinkedBlockingQueue(100));

    private SurfaceTexture surfaceTexture;
    int width = 720;
    int height = 1280;
    private byte[] y;
    private byte[] u;
    private byte[] v;
    byte[] nv12;
    LifecycleOwner lifecycleOwner;
    ICallBack callBack;
    public CameraXHelper(LifecycleOwner lifecycleOwner, int width, int height, SurfaceTexture surfaceTexture) {
        this.lifecycleOwner = lifecycleOwner;
        this.width = width;
        this.height = height;
        this.surfaceTexture = surfaceTexture;
        ListenableFuture<ProcessCameraProvider> instance = ProcessCameraProvider.getInstance((Context) lifecycleOwner);

        instance.addListener(()->{

            try {
                ProcessCameraProvider processCameraProvider = instance.get();
                bind(processCameraProvider);
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }, ContextCompat.getMainExecutor((Context) lifecycleOwner));

    }

    public void setCallBack(ICallBack callBack) {
        this.callBack = callBack;
    }

    private void bind(ProcessCameraProvider cameraProvider) {
        ImageAnalysis imageAnalysis =
                new ImageAnalysis.Builder()
                        // enable the following line if RGBA output is needed.
                        //.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
                        .setTargetResolution(new Size(width, height))
                        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                        .build();

        imageAnalysis.setAnalyzer(threadPoolExecutor, new ImageAnalysis.Analyzer() {
            @Override
            public void analyze(@NonNull ImageProxy imageProxy) {

                myAnalyze(imageProxy);
                imageProxy.close();
            }
        });
        Preview preview = new Preview.Builder()
                .setTargetResolution(new Size(width,height))
                .build();
        preview.setSurfaceProvider(new Preview.SurfaceProvider() {
            @Override
            public void onSurfaceRequested(@NonNull SurfaceRequest request) {
                Size resolution = request.getResolution();
                surfaceTexture.setDefaultBufferSize(resolution.getWidth(),resolution.getHeight());
                Surface surface = new Surface(surfaceTexture);
                request.provideSurface(surface, ContextCompat.getMainExecutor((Context) lifecycleOwner), new Consumer<SurfaceRequest.Result>() {
                    @Override
                    public void accept(SurfaceRequest.Result result) {
                        surface.release();
                        surfaceTexture.release();
                    }
                });
            }
        });

        CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build();
        cameraProvider.bindToLifecycle(lifecycleOwner,cameraSelector, imageAnalysis, preview);
    }
    private ReentrantLock lock =  new ReentrantLock();
    private void myAnalyze(ImageProxy imageProxy) {
        lock.lock();
        ImageProxy.PlaneProxy[] planes = imageProxy.getPlanes();

        if (y == null) {
            y = new byte[planes[0].getBuffer().limit() - planes[0].getBuffer().position()];
            u = new byte[planes[1].getBuffer().limit() - planes[1].getBuffer().position()];
            v = new byte[planes[2].getBuffer().limit() - planes[2].getBuffer().position()];
        }
        if (imageProxy.getPlanes()[0].getBuffer().remaining() == y.length) {
            planes[0].getBuffer().get(y);
            planes[1].getBuffer().get(u);
            planes[2].getBuffer().get(v);

            Size size = new Size(imageProxy.getWidth(),imageProxy.getHeight());
            int w = size.getWidth();
            int h = size.getHeight();

            // 吧yuv 组装成 nv12   传递给 编码器 编码 yyyy  yyyy  yyyy  yyyy  uvuv  uvuv
            if(nv12==null) {
                nv12 = new byte[ w * h * 3 / 2];
            }
            if (nv12 == null) {
                nv12 = new byte[width * height * 3/2];
            }

//        // 拷贝y
            System.arraycopy(y,0,nv12,0,y.length);
            //y+ 1/4 +1/4 = y*6/4   剩余1/2 空间

            // 一共长度 是 分别 u v 取一半 放进去 因为是 uv 间隔
            int length =  y.length +u.length/2 + v.length/2;

//            Log.e(TAG,"length  -y:"+ y.length);
//            Log.e(TAG,"length  -u"+ u.length);
//            Log.e(TAG,"length  -v"+ v.length);
//            Log.e(TAG,"总大小 "+ w*h*3/2);
            int uvIndex= 0;
            // 把后面得 uv 都写进去
            for (int i = y.length; i< h*w*3/2;i+= 2)  {
                nv12[i] = u[uvIndex];
                nv12[i+1] = v[uvIndex];
                uvIndex ++;
            }
            if (callBack!=null) {
                callBack.call(nv12,size);
            }
            // 利用x264 编码
        }
        lock.unlock();
    }


    public interface ICallBack {
        void call(byte[] data,Size size);
    }

}
